import {strFrom, strTo } from './str.js'
import {jsonArray} from './json.js'
import br from './browser.js'
import uid from './uid.js'
import lodash from 'lodash'

const _EventUser = {}
const _event_remove = function(k) {
  if (k.fn) {
    lodash.pull(_EventUser[k.id][k.type], k)
    k.type = k.fn = k.args = null
  }
}
const _event_name = function(a) {
  return a.indexOf('.') > -1 ? strTo(a, '.') : a
}
const _event_space = function(a) {
  return a.indexOf('.') > -1 ? '.' + strFrom(a, '.').split('.').sort().join('.') + '.' : ''
}
const _event_handlers = function(a, b) {
  var c = _EventUser[uid(a)];
  if (!c) return;
  var type = _event_name(b),
    ns = _event_space(b);
  if (type) {
    return c[type] && (ns ? c[type].filter(v => v.ns.indexOf(ns) > -1) : c[type])
  }
  var r = [];
  for (type in c) {
    r.push.apply(r, c[type].filter(v => v.ns.indexOf(ns) > -1))
  }
  return r;
}

class EventListener {
  constructor(ctx) {
    this.ctx = ctx
  }
  hasEvent(type) {
    let a = _event_handlers(this.ctx, type)
    return a && a.length
  }
  // type -> event type, fn -> fn, pvdr -> context, one -> exec once [T/F]
  addEvent(type, fn, one) {
    var k = {
      id: uid(this.ctx),
      type: _event_name(type),
      ns: _event_space(type),
      fn: fn,
      one: one
    };
    jsonArray(k, _EventUser, k.id, k.type).ori = true
    return this;
  }
  addEventOnce(type, fn) {
    return this.addEvent(type, fn, true)
  }
  // e -> event, r -> args[array]
  fireEvent(e, r) {
    var a = _event_handlers(this.ctx, e.type || e);
    if (a) {
      for (var i = 0, k, l = a.length, v; i < l; i++) {
        if ((k = a[i]) && k.fn) {
          v = k.fn.apply(this.ctx, r ? [e].concat(r) : [e]);
          if (k.one) {
            _event_remove(k);
            a.ori && (i--, l--);
          }
          if (v !== undefined)
            return v;
        }
      }
    }
    return this;
  }
  // a -> event type, b -> fn
  removeEvent(a, b) {
    if (a) {
      var d = _event_handlers(this.ctx, a);
      if (d) {
        var i = d.length;
        while (i--)
          if (!b || (d[i].fn === b)) _event_remove(d[i]);
      }
    } else {
      var c = _EventUser[uid(this.ctx)],
        i;
      for (i in c) this.removeEvent(i);
      delete _EventUser[uid(this.ctx)];
    }
    return this;
  }
}

const eventX = function(e) {
  return e.targetTouches ? e.targetTouches[0].clientX : e.clientX
}

const eventY = function(e) {
  return e.targetTouches ? e.targetTouches[0].clientY : e.clientY
}

const moveup = function(moveCB, upCB, downEv) {
  let moveHdl, upHdl, dir = downEv == null,
    cvs = document.documentElement,
    evX = e => e.targetTouches ? e.targetTouches[0].clientX : e.clientX,
    evY = e => e.targetTouches ? e.targetTouches[0].clientY : e.clientY,
    prevent = e => e.preventDefault()
  if (downEv.currentTarget) {
    downEv.currentTarget.style.userSelect = 'none'
  }
  cvs.addEventListener(br.mobile ? 'touchmove' : 'mousemove', moveHdl = function(e) {
    dir ? moveCB(e) :
      downEv && (Math.abs(evX(e) - evX(downEv)) > 1 || Math.abs(evY(e) - evY(downEv)) > 1) && (dir = true)
  }, true);
  cvs.addEventListener(br.mobile ? 'touchend' : 'mouseup', upHdl = function(e) {
    cvs.removeEventListener(br.mobile ? 'touchmove' : 'mousemove', moveHdl, true)
    cvs.removeEventListener(br.mobile ? 'touchend' : 'mouseup', upHdl, true)
    if (downEv.currentTarget) {
      downEv.currentTarget.style.userSelect = ''
    }
    br.mobile && cvs.removeEventListener('touchmove', prevent, {
      passive: false
    });
    dir && upCB && upCB(e)
  }, true);
  // 业务拖动时禁用浏览器默认的拖动效果
  br.mobile && cvs.addEventListener('touchmove', prevent, {
    passive: false
  })
}

export {EventListener, eventX, eventY, moveup}